# -*- coding: utf-8 -*-

import os
import sys

if __name__ == "__main__":
    currentdir = os.path.dirname( os.path.abspath( sys.argv[0] ) )
    basedir = os.path.abspath( os.path.join( currentdir, ".." ) )
    pythondir = basedir
    sys.path.insert( 0, pythondir )

import traceback
import types
import mimetypes
from time import time
from jsonrpc.jmsghandler import RequestHandler
from jsonrpc.sendmsg import sendjmsg
from jsonrpc import Server
from time import sleep
from project.constants import *

__all__ = ['ProcessArchiveHandler', 'ProcessArchiveServer', 'ProcessArchiveRequest', 'ProcessArchiveManager']

#---------------------------------------------

class ProcessArchiveHandler(RequestHandler):
    """
    Do some tasks, maybe in subprocess
    """
    
    def _json_handle(self, json_data):
        """
        @param json_data: simple object data, after json.loads
        @type: mixed
        
        @return: python simple object data, jsonable via json.dumps
        @rtype: mixed
        """
        result = False
        
        if not (type(json_data) is types.DictionaryType):
            return result

        if json_data["task"] == "ping":
            return "pong"
        if json_data["task"] == "quit":
            return self.server.quit()
        if json_data["task"] == "process":
            json_data = json_data["data"]
            return self.server.process(
                json_data["file_id"],
                json_data["file_path"],
                json_data["mime"]
            )
        return result
    
    pass

#---------------------------------------------

class ProcessArchiveServer(Server):
    
    queue = None
    stop_event = None
    task_service_address = None
    _worker_thread = None


    p = None
    stats_req = None


    def get_mime_type( self, full_path ):
        mime = None
        try:

            import magic
            mo = magic.open( magic.MAGIC_MIME_TYPE )
            mo.load()
            mime = mo.file( full_path )
            mo.close()

        except Exception:
            pass
        if not mime:
            mime = mimetypes.guess_type( full_path )[0]
        if not mime:
            mime = "application/octet-stream"
        return mime

    def run_image(self, file_id, tmp_dir, image_path):

        sess = self.p.get_session()

        self.stats_req.inc("images_count", 1)

        try:

            from images import load_image
            from images.vector import process_image

            fsize = os.path.getsize( image_path )
            mime = self.get_mime_type( image_path )

            res = load_image(image_path)

            im = res["image"]
            w = res["width"]
            h = res["height"]
            mode = res["mode"]

            self.stats_req.inc("images_size", fsize )

            res = process_image(im, parts=10)

            anim = False
            try:
                im.seek(1)
                anim = True
            except EOFError:
                pass

            name = image_path.replace(tmp_dir, "")

            img_item = self.p.insert_image(
                file_id, name, anim, mime, fsize, res["color"] == False ,
                w, h, mode, res["mean_gray"]
            )

            v10_item = self.p.insert_image_v10(
                img_item.id, IMAGE_VECTOR_ORIENTATION_NORMAL ,
                *res["vector"]
            )
            sess.expunge( v10_item )

            res = load_image(image_path)
            im = res["image"]

            res = process_image(im, mirror=True, parts=10)

            v10_item = self.p.insert_image_v10(
                img_item.id, IMAGE_VECTOR_ORIENTATION_FLIP_LEFT_RIGHT ,
                *res["vector"]
            )
            sess.expunge( v10_item )


            res = process_image(im)

            v100_item = self.p.insert_image_v100(
                img_item.id, IMAGE_VECTOR_ORIENTATION_NORMAL ,
                ",".join([ str(v) for v in res["vector"] ])
            )
            sess.expunge( v100_item )

            res = process_image(im, mirror=True)

            v100_item = self.p.insert_image_v100(
                img_item.id, IMAGE_VECTOR_ORIENTATION_FLIP_LEFT_RIGHT ,
                ",".join([ str(v) for v in res["vector"] ])
            )
            sess.expunge( v100_item )


            sess.expunge( img_item )

            self.stats_req.inc("images_processed", 1)

        except Exception:
            self.stats_req.inc("errors_count", 1)
            print("ProcessArchiveServer::run_image():\nFile=%r\n ERROR!\n%s" % ( image_path, traceback.format_exc() ) )
            pass

        return


    def _worker ( self ):

        print("ProcessArchiveServer: START: %s" % (self.server_address,))

        from project.alchemy import Project
        from archive import which
        from images import is_image
        from subprocess import Popen
        from service.task import TaskManager

        mimetypes.init()

        self.p = Project()
        self.p.connect()

        bin_rm = which("rm")
        bin_python = which("python")

        task_manager = TaskManager(self.task_service_address)

        self.stats_req = task_manager.get_request().getStatistics()


        while not self.stop_event.isSet():

            try:
                data = self.queue.get_nowait()
            except Exception:
                data = None

            if data is None:
                sleep(0.01)
                continue


            file_id, file_path, mime = data

            try:
                self.stats_req.set_imaging( file_path, 0, 0, time() )

                tmp_dir = os.tempnam()
                if not os.path.isdir(tmp_dir):
                    os.makedirs(tmp_dir)

                pp = Popen([
                    bin_python, "archive/extract_tool.py",
                    "--archive", file_path,
                    "--mime", mime,
                    "--into-dir", tmp_dir
                ])
                while pp.poll() is None:
                    sleep(0.01)
                    if self.stop_event.isSet():
                        pp.terminate()
                        break

                for root, dirs, files in os.walk(tmp_dir):

                    if self.stop_event.isSet():
                        break

                    self.stats_req.set_imaging( file_path, len(files), 0, time() )

                    for f in files:

                        if self.stop_event.isSet():
                            break

                        full_path = os.path.join(root, f)

                        if is_image(full_path):
                            self.run_image( file_id, tmp_dir, full_path )

                        self.stats_req.inc_imaging(1)


                Popen([bin_rm, "-r", "-f", tmp_dir]).wait()

            except Exception:
                self.stats_req.inc("errors_count", 1)
                print("ProcessArchiveServer: ERROR!\n%s" % traceback.format_exc())
                pass

            self.stats_req.inc("archives_processed", 1)

            self.queue.task_done()

            self.p.get_session().close()

        self.p.close_session()

        print("ProcessArchiveServer: STOP: %s" % (self.server_address,))

        self.stop_event.clear()
        return

    def server_activate(self):
        from Queue import Queue
        from threading import Thread, Event

        self.queue = Queue()
        self.stop_event = Event()
        self._worker_thread = Thread(target=self._worker)
        self._worker_thread.start()

        return Server.server_activate(self)

    def process(self, file_id, file_path, mime):
        self.queue.put_nowait((file_id, file_path, mime,))
        return True

    def _quit_thread(self):
        sleep(0.05)
        self.server_close()
        return

    def quit(self):
        print("ProcessArchiveServer: quit event")

        self.stop_event.set()

        # Clean Queue

        while True:
            try:
                data = self.queue.get_nowait()
            except Exception:
                data = None

            if data is None:
                break

            self.queue.task_done()

        from threading import Thread

        th = Thread(target = self._quit_thread)
        th.daemon = True
        th.start()
        return True

    pass


#---------------------------------------------

class ProcessArchiveRequest(object):

    manager = None

    def __init__(self, manager):
        self.manager = manager
        pass

    def process(self, file_id, file_path, mime):
        return self.manager.sendmsg( "process", {
            "file_id" : file_id,
            "file_path" : file_path,
            "mime" : mime
        })

    def ping(self):
        return self.manager.sendmsg( "ping", {})

    def quit(self):
        return self.manager.quit()

    pass

#---------------------------------------------

class ProcessArchiveManager(object):

    host = "127.0.1.7"
    port_min = 10000
    port_max = 20000
    port = None
    request = None

    address = None

    task_service_address = None

    def __init__(self, task_service_address):
        self.task_service_address = task_service_address
        pass

    def start_service(self):

        runing = False
        if not self.port:

            import random

            self.port = random.randint(self.port_min, self.port_max)

            self.address = (self.host, self.port,)

            from archive import which
            from subprocess import Popen

            Popen([which("python"), "service/process_archive.py",
                   "--host", "%s" % self.host,
                   "--port", "%s" % self.port,
                   "--task-service", "%s:%s" % self.task_service_address
            ])

            # WAIT WHILE SERVICE STARTS

            pings = 10
            t_begin = time()
            while not runing:
                pong = self.sendmsg("ping", {})

                if pong == "pong":
                    runing = True

                pings -= 1
                if pings <= 0:
                    break

            if pings <= 0:
                self.port = None
                print("ProcessArchiveManager: FAIL TO START service on %r in %s sec" % (self.address, time() - t_begin,))
            else:
                print("ProcessArchiveManager: STARTED service on %r in %s sec" % (self.address, time() - t_begin,))

        return runing

    def sendmsg(self, task, data):
        count = 5
        t_wait = 0.2

        result = None
        while count > 0:
            t_begin = time()
            try:
                result = sendjmsg( self.address, {"task":task, "data":data}, timeout=t_wait )
                break
            except Exception:
                result = None
                count -= 1
                t_end = time()
                if t_end - t_begin < t_wait:
                    sleep(t_wait - (t_end - t_begin))
        if count <= 0:
            print("ProcessArchiveManager: FAIL SEND MESSAGE: task=%s, data=%r" % (task, data,))
        return result

    def quit(self):
        return self.sendmsg("quit", {})

    def get_request(self):
        if not self.request:
            self.request = ProcessArchiveRequest(self)
        return self.request

    pass


#---------------------------------------------

def run_process_archive_service(host, port, task_service_address):

    try:
        SERVER_ADDRESS = (host, int(port),)

        server = ProcessArchiveServer(SERVER_ADDRESS, ProcessArchiveHandler, bind_and_activate=False)
        if task_service_address:
            host, port = task_service_address.split(':')
            server.task_service_address = (host, int(port),)

        server.server_bind()
        server.server_activate()
        server.serve_forever()
    except Exception:
        pass

    return


#---------------------------------------------

def launch():

    print("ProcessArchive: launch service...")

    from argparse import ArgumentParser

    parser = ArgumentParser(description='Run image archive processing service.')
    parser.add_argument('--host', dest='host', action='store', type=str,
        help='Listen on TCP address')
    parser.add_argument('--port', dest='port', action='store', type=int,
        help='Listen on TCP port')
    parser.add_argument('--task-service', dest='task_service', action='store', type=str,
        help='Task service listen on TCP address')

    args = parser.parse_args()
    run_process_archive_service(args.host, args.port, args.task_service)

    return 0

if __name__ == "__main__":
    launch()
